#!/usr/bin/env bash

mydir="$(dirname $(realpath $(dirname "$(realpath $0)")))"
if ! [ -e "${mydir}/shell_modules/include.sh" ]; then
    cd "${mydir}" || exit 1
    git submodule init || exit 1
    git submodule update || exit 1
fi
source "${mydir}/shell_modules/include.sh"
include::research_path_add "${mydir}"

include "stack.sh"
include "test.sh"

Generating_PumlPath=""
function Generating::SetPuml()
{
    Generating_PumlPath="$1"
}

function Generating::_PrintPuml()
{
    echo "$*" >> "$Generating_PumlPath"
}

Generating_CurrentID=""
Generating_SettingID=""
Generating_Stack=""
function Generating::FromCppToPuml::Call()
{
    local line="$1"

    local target=""
    local call=""
    local text
    for text in ${line}; do
        # .*::{id}(.*
        if [[ "$text" =~ (^[*&]?[a-zA-Z0-9_]+::[a-zA-Z0-9_]+[\(]) ]]; then
            target="$(cut -d'(' -f1 <<< "$text")"
            if [[ "${target:0:1}" =~ ([*&]) ]]; then
                target="${target:1}"
            fi
            Generating_CurrentID="${target//:*/}"
            if [ "$Generating_CurrentID" == "" ]; then 
                Generating_CurrentID="global"
            fi

            call="${target//*:/}"
        fi

        # .{id}(.*
        if [[ "$text" =~ (^[*&]?[a-zA-Z0-9_]+[\(]) ]]; then
            target="$(cut -d'(' -f1 <<< "$text")"
            if [[ "${target:0:1}" =~ ([*&]) ]]; then
                target="${target:1}"
            fi
            Generating_CurrentID="global"
            call="${target}"
        fi
    done

    if [ "$target" == "" ]; then
        loge "cannot found target: $line"
        return 1
    fi

    if [ "$Generating_SettingID" != "" ]; then
        Generating_CurrentID="$Generating_SettingID"
        Generating_SettingID=""
    fi

    debuglog "call ${Generating_CurrentID}::${call}"
    Generating::_PrintPuml "$(Stack::Top Generating_Stack) -> ${Generating_CurrentID}: ${call}"
}

function Generating::FromCppToPuml::Core()
{
    local line="$1"
    if [ "${line:0:5}" == "//=> " ]; then
        Generating::FromCppToPuml::Call "${line:5}"
    fi

    # /*< note right >*/
    if [ "${line:0:4}" == "/*< " ]; then
        debuglog "note right $(Stack::Top Generating_Stack): ${line:4:-4}"
        Generating::_PrintPuml "note right $(Stack::Top Generating_Stack): ${line:4:-4}"
    fi

    # /*= role =*/
    if [ "${line:0:4}" == "/*= " ]; then
        debuglog "push id ${line:4:-4}"
        Stack::Push Generating_Stack "${line:4:-4}"
    fi

    # /*% file %*/
    if [ "${line:0:4}" == "/*% " ]; then
        debuglog "set id ${line:4:-4}"
        Generating_SettingID=""
        for ((i = 0; i < ${#files[*]}; i++)); do
            if [ "${line:4:-4}" == "${files[$i]}" ]; then
                Generating_SettingID="f$i"
            fi
        done

        if [ "$Generating_SettingID" == "" ]; then
            Generating_SettingID="f${#files[*]}"
            Generating::_PrintPuml "participant $Generating_SettingID as \"${line:4:-4}\""
            files+=(${line:4:-4})
        fi
    fi

    # /*! exec !*/
    if [ "${line:0:4}" == "/*! " ]; then
        debuglog "exec ${line:4:-4}"
        Generating::_PrintPuml "${line:4:-4}"
    fi

    # //{
    if [ "${line:0:3}" == "//{" ]; then
        debuglog "$Generating_CurrentID {"
        Generating::_PrintPuml "${Generating_CurrentID}++"
        Stack::Push Generating_Stack "$Generating_CurrentID"
    fi

    # //}
    if [ "${line:0:3}" == "//}" ]; then
        debuglog "}"
        Stack::Pop Generating_Stack
        Generating::_PrintPuml "return"
    fi
}

function Generating::FromCppToPuml()
{
    local cppfile="$1"
    local Generating_Stack
    Stack::New Generating_Stack
    Stack::Push Generating_Stack "global"
    local Generating_CurrentID="global"
    while read line; do
        Generating::FromCppToPuml::Core "$line"
    done <<< $(egrep "//[={}]|/\*[<!%=]" $cppfile)
    Stack::Del Generating_Stack
    [ "$Generating_CurrentID" != "" ]
}

function Generating::FromCppToPuml::Test1()
{
    local puml_result=""
    function Generating::_PrintPuml()
    {
        puml_result="$*"
    }

    Stack::New Generating_Stack
    Stack::Push Generating_Stack "global"
    Generating_CurrentID="global"
    Generating::FromCppToPuml::Core "//=> std::function<std::vector(int, int)> A::B()"
    test::var_should_be puml_result "global -> A: B"
    test::var_should_be Generating_CurrentID "A"

    Generating::FromCppToPuml::Core "//{"
    test::var_should_be puml_result "A++"
    test::run_cmd Stack::Top Generating_Stack
    test::should_be "A"

    Generating::FromCppToPuml::Core "//=> void ParagraphTxt::Layout(double width)"
    test::var_should_be puml_result "A -> ParagraphTxt: Layout"
    test::var_should_be Generating_CurrentID "ParagraphTxt"

    Generating::FromCppToPuml::Core "//}"
    test::var_should_be puml_result "return"
    test::run_cmd Stack::Top Generating_Stack
    test::should_be "global"

    Generating::FromCppToPuml::Core "/*< part 1: state clear >*/"
    test::var_should_be puml_result "note right global: part 1: state clear"

    Generating::FromCppToPuml::Core "/*! loop part 2: foreach line !*/"
    test::var_should_be puml_result "loop part 2: foreach line"
}
test::register_test Generating::FromCppToPuml::Test1

function Generating::FromCppToPuml::Test2()
{
    local puml_result=""
    function Generating::_PrintPuml()
    {
        puml_result="$*"
    }

    Stack::New Generating_Stack
    Stack::Push Generating_Stack "global"
    Generating_CurrentID="global"
    test::run_cmd Stack::Top Generating_Stack
    test::should_be "global"

    Generating::FromCppToPuml::Core "/*= A =*/"
    test::run_cmd Stack::Top Generating_Stack
    test::should_be "A"

    Generating::FromCppToPuml::Core "/*% b.cpp %*/"
    test::var_should_be puml_result 'participant f0 as "b.cpp"'
    test::run_cmd Stack::Top Generating_Stack
    test::should_be "A"
}
test::register_test Generating::FromCppToPuml::Test2

